all files / src/ julian.ts

96.15% Statements 50/52
94.44% Branches 17/18
90% Functions 9/10
95.92% Lines 47/49
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100  1× 1× 1× 1×             1×   1× 1× 9507×     612633× 612632× 612632×   612632×             1× 159124× 159124×   159124× 159124× 159124× 159124× 159124×   159124× 159124× 133290×   25834×   159124× 159124× 133290×   25834×   159124×   159124×           1× 612632×       612632×           1×   159124× 25834× 25834×     159124×     1× 612632×         1× 159124× 159124×     1× 1×     1×     1×  
import { Calendar } from "./calendar";
import { isLeapYear } from "./common";
import { IHistoricalDate } from './historical-date';
import { InvalidDateException } from './invalid-date-exception';
import { GregorianDate } from "./gregorian";
 
// Licensed under the MIT license:
// http://opensource.org/licenses/MIT
// Copyright (c) 2016, fitnr <fitnr@fakeisthenewreal>
// based on https://github.com/fitnr/convertdate/blob/master/convertdate/julian.py
 
const HAVE_30_DAYS = [4, 6, 9, 11];
 
export class JulianDate extends IHistoricalDate<'julian'> {
    public get isLeapYear() {
        return isLeapYear(this.year, this.calendar);
    }
 
    constructor(public readonly year: number,
        public readonly month: number,
        public readonly day: number) {
        super('julian');
        this.assertLegalDate(year, month, day);
    }
 
    /**
     * Creates a new JulianDate object based on the number of Julian days.
     * @param jd 
     */
    static fromJulianDays(jd: number) {
        jd += 0.5;
        let z = Math.trunc(jd);
 
        let a = z;
        let b = a + 1524;
        let c = Math.trunc((b - 122.1) / 365.25);
        let d = Math.trunc(365.25 * c);
        let e = Math.trunc((b - d) / 30.6001);
 
        let month: number;
        if (e < 14) {
            month = e - 1;
        } else {
            month = e - 13;
        }
        let year: number;
        if (month > 2) {
            year = c - 4716;
        } else {
            year = c - 4715;
        }
        let day = b - d - Math.trunc(30.6001 * e);
 
        return new JulianDate(year, month, day);
    }
 
    /**
     * Check if this is a legal date in the Julian calendar
     */
    private assertLegalDate(year: number, month: number, day: number) {
        Iif (day < 0 || day > this.monthLength(year, month)) {
            throw new InvalidDateException(`Month ${month} doesn't have a day ${day}`);
        }
 
        return true;
    }
 
    /**
     * Convert to Julian day using astronomical years (0 = 1 BC, -1 = 2 BC)
     */
    private toJulianDay(year: number, month: number, day: number) {
        // Algorithm as given in Meeus, Astronomical Algorithms, Chapter 7, page 61
        if (month <= 2) {
            year -= 1;
            month += 12;
        }
 
        return (Math.trunc((365.25 * (year + 4716))) + Math.trunc((30.6001 * (month + 1))) + day) - 1524.5
    }
 
    public monthLength(year: number, month: number) {
        return month == 2
            ? isLeapYear(year, 'julian') ? 29 : 28
            : HAVE_30_DAYS.indexOf(month) >= 0 ? 30 : 31;
    }
 
    public toGregorian() {
        let julianDays = this.toJulianDay(this.year, this.month, this.day);
        return GregorianDate.fromJulianDays(julianDays);
    }
 
    public toJulian() {
        return this;
    }
 
    public toString() {
        return `${this.year}-${this.month}-${this.day} (Julian)`;
    }
}